/**********************************************************
* @ File name -> gun.cpp
* @ Version   -> V1.0
* @ Date      -> 20160718
* @ Brief     -> 充电枪控制相关函数

 V1.*
* @ Revise    ->
**********************************************************/
//#include "stm32f10x.h"
#include "mod_gun.h"
#include "platform.h"
#include "data.h"
#include "port.h"
#include "pwm.h"
#include "adc.h"
#include "mod_port.h"

#include <string.h>
#include <stdlib.h>

#define DRV_GUN_CP_12V_VALUE 2620  //2590
#define DRV_GUN_CP_9V_VALUE  1950  //1940
#define DRV_GUN_CP_6V_VALUE  1300  //1370

#define DRV_GUN_CP_INTERVAL  300

#define TEM_BUF_SIZE 10

#define RELAY_CHECK_DELAY_CNT 20

enum RelaySetState{
    RELAY_SET_STATE_CLOSE = 1, //继电器设置状态闭合
    RELAY_SET_STATE_OPEN,      //继电器设置状态打开
    RELAY_SET_STATE_CHECK,     //继电器设置判断粘连状态
};

typedef struct {
    uint8_t cpLevel;
    uint16_t temBuf[TEM_BUF_SIZE];
    uint8_t temCnt;
    uint16_t cp_value; //cp实际值
	
    uint32_t val_12v; //
    uint32_t val_9v;
    uint32_t val_6v;
} T_GunCP;

typedef struct {
    T_GunCP cp;
    uint8_t state;
    uint8_t tempState;
    uint8_t tempAlarmState;
    
    uint8_t relay_set_state;
    uint8_t relay_alarm;
    uint8_t delay_cnt;
} T_Gun;

static T_Gun Gun = {0};

typedef struct pwmStateChg {
    enum GunLinkState oldStat;
    enum GunLinkState newStat;
    enum GunPwmState act;
} T_pwmStateChg;

static const T_pwmStateChg g_pwmStatChgArray[] = {
    {DRV_GUN_STATE_FREE, DRV_GUN_STATE_INDEV, DRV_GUN_PWM_OFF},
    {DRV_GUN_STATE_FREE, DRV_GUN_STATE_INCAR, DRV_GUN_PWM_ON},
    {DRV_GUN_STATE_FREE, DRV_GUN_STATE_READY, DRV_GUN_PWM_ON},
    {DRV_GUN_STATE_FREE, DRV_GUN_STATE_ERR, DRV_GUN_PWM_OFF},

    {DRV_GUN_STATE_INDEV, DRV_GUN_STATE_INCAR, DRV_GUN_PWM_ON},
    {DRV_GUN_STATE_INDEV, DRV_GUN_STATE_READY, DRV_GUN_PWM_ON},
    {DRV_GUN_STATE_INDEV, DRV_GUN_STATE_ERR, DRV_GUN_PWM_OFF},

    {DRV_GUN_STATE_INCAR, DRV_GUN_STATE_FREE, DRV_GUN_PWM_OFF},
    {DRV_GUN_STATE_INCAR, DRV_GUN_STATE_INDEV, DRV_GUN_PWM_OFF},
    {DRV_GUN_STATE_INCAR, DRV_GUN_STATE_READY, DRV_GUN_PWM_ON},
    {DRV_GUN_STATE_INCAR, DRV_GUN_STATE_ERR, DRV_GUN_PWM_OFF},

    {DRV_GUN_STATE_READY, DRV_GUN_STATE_FREE, DRV_GUN_PWM_OFF},
    {DRV_GUN_STATE_READY, DRV_GUN_STATE_INDEV, DRV_GUN_PWM_OFF},
    {DRV_GUN_STATE_READY, DRV_GUN_STATE_INCAR, DRV_GUN_PWM_ON},
    {DRV_GUN_STATE_READY, DRV_GUN_STATE_ERR, DRV_GUN_PWM_OFF},
};

int32_t get_cp_default_value(T_GunCP *cp){
    cp->val_12v = data_getint("cp12v");
    cp->val_9v = data_getint("cp9v");
    cp->val_6v = data_getint("cp6v");
    return 0;
}

int32_t get_pwm_power_value(void){
    int32_t val = data_getint("pwm");
    if (val == 0){
        val = 500;
    }
    return val;
}

static void gun_cc_init(void){
    cc_port_init();
}

static void gun_cp_init(T_GunCP *cp) {

    pwm_init(get_pwm_power_value());
    adc_init();
    
    cp->cpLevel = DRV_GUN_CP_12V;
    get_cp_default_value(cp);
}

static void gun_set_pwm_state(enum GunPwmState state) {
    if (state == DRV_GUN_PWM_ON) {
        pwm_on();
    } else if (state == DRV_GUN_PWM_OFF) {
        pwm_off();
    }else{

    }
}

static uint8_t gun_get_cp_level(T_GunCP *cp) {

    uint16_t value = adc_get(ADC_CP);
    if (abs(value - Gun.cp.cp_value) > 50){
        Gun.cp.cp_value = value;
        return cp->cpLevel;
    }
    
    Gun.cp.cp_value = value;
    if (value >= (cp->val_12v + DRV_GUN_CP_INTERVAL)){
        cp->cpLevel = DRV_GUN_CP_ERR;
    } else if ((value < (cp->val_12v + DRV_GUN_CP_INTERVAL))
            && (value >= (cp->val_12v - DRV_GUN_CP_INTERVAL))) {
        cp->cpLevel = DRV_GUN_CP_12V;
    } else if ((value < (cp->val_9v + DRV_GUN_CP_INTERVAL))
               && (value >= (cp->val_9v - DRV_GUN_CP_INTERVAL))) {
        cp->cpLevel = DRV_GUN_CP_9V;
    } else if ((value < (cp->val_6v + DRV_GUN_CP_INTERVAL))
               && (value >= (cp->val_6v - DRV_GUN_CP_INTERVAL))) {
        cp->cpLevel = DRV_GUN_CP_6V;
    } else if(value < (cp->val_6v - DRV_GUN_CP_INTERVAL)){
        cp->cpLevel = DRV_GUN_CP_ERR;
    }

    return cp->cpLevel;
}

uint8_t GunCP_GetTemperatureState(T_GunCP *cp) {
    uint32_t totalvalue = adc_get(ADC_TEMP1);
    
    uint16_t Temvalue = (uint16_t)((totalvalue * 3300 / 4096) - 600) / 10;
    uint16_t sun = 0;
    cp->temBuf[cp->temCnt] = Temvalue;
    cp->temCnt = (cp->temCnt + 1) % TEM_BUF_SIZE;
    for (uint8_t j = 0; j < TEM_BUF_SIZE; j++) {
        sun = sun + cp->temBuf[j];
    }
    Temvalue = sun / 10;
    //printf("Temvalue = %d\r\n",Temvalue);
    if(Temvalue >= LOCK_LARGE_TEM) { //超温
        return LOCK_TEM_OVER;
    } else {
        return LOCK_TEM_INSIDE;
    }
}

static void gun_set_pwm(uint8_t oldState, uint8_t newState) {
    for(uint8_t i = 0; i < sizeof(g_pwmStatChgArray) / sizeof(T_pwmStateChg); i++) {
        if(oldState == (uint8_t)g_pwmStatChgArray[i].oldStat && newState == (uint8_t)g_pwmStatChgArray[i].newStat) {
            enum GunPwmState pwmstate = g_pwmStatChgArray[i].act;
            //gun_set_pwm_state(pwmstate);
        }
    }
}

static void Gun_GunStatePrint(const T_Gun *gun) {
    const char *buf[] = {"null", "indev", "incar", "ready"};
    printf("gun state: %s\r\n", buf[gun->state]);

}

void gun_state_poll(void){
    uint8_t state = DRV_GUN_STATE_FREE;
    uint8_t cc = cc_read_state();
    uint8_t cp = gun_get_cp_level(&Gun.cp);

    if (cc == DRV_GUN_CC_STATE_GUN_OFF) {
        state = DRV_GUN_STATE_FREE;
    } else {
        if(cp == DRV_GUN_CP_12V) {
            state = DRV_GUN_STATE_INDEV;
        } else if(cp == DRV_GUN_CP_9V) {
            state = DRV_GUN_STATE_INCAR;
        } else if (cp == DRV_GUN_CP_6V) {
            state = DRV_GUN_STATE_READY;
        } else {
            state = DRV_GUN_STATE_ERR;
        }
    }

    if (Gun.state != state) {
        gun_set_pwm(Gun.state, state);
        Gun.state = state; 
        Gun_GunStatePrint(&Gun);
    }
}

void relay_state_poll(void){

    //继电器状态刚切换时，延迟一段时间再进行状态判断
    if (Gun.delay_cnt){
        Gun.delay_cnt--;
        return;
    }

    uint8_t read_l =  port_get_state(PORT_STATE_RELAY_L);
    uint8_t read_n = port_get_state(PORT_STATE_RELAY_N);

    
    //未开启充电，检测继电器是否粘连
    if (Gun.relay_set_state == RELAY_SET_STATE_OPEN){
        #ifdef GTC_DP1
        if (port_is_close(PORT_STATE_RELAY_L)){
        #else
        if (port_is_close(PORT_STATE_RELAY_L) || port_is_close(PORT_STATE_RELAY_N)){
        #endif
            //if (Gun.relay_alarm != RELAY_ALARM_STUCK){
            //    printf("relay_state_poll relay stuck\r\n");
            //    Gun.relay_alarm = RELAY_ALARM_STUCK;
            //}
        }

    //已开启充电，检测继电器是否未闭合
    }else if(Gun.relay_set_state == RELAY_SET_STATE_CLOSE){
        #ifdef GTC_DP1
        if (port_is_close(PORT_STATE_RELAY_L) == 0) {
        #else
        if ((port_is_close(PORT_STATE_RELAY_L) == 0) || (port_is_close(PORT_STATE_RELAY_N) == 0)){
        #endif
            //if (Gun.relay_alarm != RELAY_ALARM_UNCLOSE){
            //    printf("relay_state_poll relay unclose\r\n");
            //    Gun.relay_alarm = RELAY_ALARM_UNCLOSE;
            //}
        }
    }
}


void gun_task(void *para){
    while(1){
        gun_state_poll();
        relay_state_poll();

        os_msleep(30);
    }
}

uint32_t gun_get_cp_value(void){
	return Gun.cp.cp_value;
}

uint8_t gun_get_state(uint8_t gunid){
    return Gun.state;
}

int32_t gun_start_charge(uint8_t gunid){
    #ifdef GTC_DP1
        uint8_t l1=0;
        uint8_t l2=0;
        uint8_t l3=0;
    
        /* 开启充电时，先闭合l继电器，检测n是否闭合，1. n未闭合则闭合n，返回成功，2. 检测到n闭合则返回失败 */
        Gun.relay_set_state = RELAY_SET_STATE_CHECK;
        Gun.delay_cnt = RELAY_CHECK_DELAY_CNT; 
    
        relay_charge_l_on();
    
        os_msleep(20);
        l1 = port_is_close(PORT_STATE_RELAY_L);
        os_msleep(20);
        l2 = port_is_close(PORT_STATE_RELAY_L);
        os_msleep(20);
        l3 = port_is_close(PORT_STATE_RELAY_L);
        printf("relay l on l1:%d l2:%d l3:%d\r\n",l1,l2,l3);
        if ((l1 && l2) || (l1 && l3) ||(l2 && l3)) {
            relay_charge_l_off();
            Gun.relay_alarm = RELAY_ALARM_STUCK;
            return -1;
        }
        relay_charge_n_on();
        printf("relay n on\r\n");
        Gun.relay_set_state = RELAY_SET_STATE_CLOSE;
        Gun.delay_cnt = RELAY_CHECK_DELAY_CNT;   

	      gun_set_pwm_state(DRV_GUN_PWM_ON);//打开pwm
        return 0;
            
    #else
  	gun_set_pwm_state(DRV_GUN_PWM_ON);//打开pwm
				
    Gun.relay_set_state = RELAY_SET_STATE_CLOSE;
    Gun.delay_cnt = RELAY_CHECK_DELAY_CNT;
    relay_charge_l_on();
    return 0;
    #endif
}

int32_t gun_stop_charge(uint8_t gunid){
    #ifdef GTC_DP1
        uint8_t l1=0;
        uint8_t l2=0;
        uint8_t l3=0;
    
        /* 关闭充电时，先打开l继电器，检测l是否粘连，1.检测到粘连，返回失败，2.未检测到粘连，则打开n，返回成功 */
        Gun.relay_set_state = RELAY_SET_STATE_CHECK;
        Gun.delay_cnt = RELAY_CHECK_DELAY_CNT;     
        relay_charge_l_off();
        os_msleep(100);
        l1 = port_is_close(PORT_STATE_RELAY_L);
        os_msleep(100);
        l2 = port_is_close(PORT_STATE_RELAY_L);
        os_msleep(100);
        l3 = port_is_close(PORT_STATE_RELAY_L);
    printf("l1:%d l2:%d l3:%d\r\n", l1, l2, l3);
        if ((l1 && l2) || (l1 && l3) ||(l2 && l3)) {
            relay_charge_n_off();
            Gun.relay_alarm = RELAY_ALARM_STUCK;
            printf("relay stuck\r\n");
            return -1;
        }    
        relay_charge_n_off();
        Gun.relay_set_state = RELAY_SET_STATE_OPEN;
        Gun.delay_cnt = RELAY_CHECK_DELAY_CNT;   

      	gun_set_pwm_state(DRV_GUN_PWM_OFF);//关闭pwm
        return 0;
    #else
		gun_set_pwm_state(DRV_GUN_PWM_OFF);//关闭pwm
			
    Gun.relay_set_state = RELAY_SET_STATE_OPEN;
    Gun.delay_cnt = RELAY_CHECK_DELAY_CNT;
    relay_charge_l_off();
	
    return 0;
    #endif
}

uint8_t gun_get_relay_alarm(uint8_t gunid){
    return Gun.relay_alarm;
}
uint8_t gun_get_relay_state(uint8_t gunid)
{  
	 return Gun.relay_set_state;
}
int32_t gun_init() {
    os_pthread_t gun_thread;
    int32_t ret = 0;

    memset(&Gun, 0, sizeof(Gun));
    Gun.state = DRV_GUN_STATE_FREE;
    Gun.tempState = LOCK_TEM_OVER;
    gun_cc_init();
    gun_cp_init(&Gun.cp);
    relay_init();

    Gun.relay_set_state = RELAY_SET_STATE_OPEN;
    Gun.relay_alarm = RELAY_ALARM_NULL;
    Gun.delay_cnt = RELAY_CHECK_DELAY_CNT;
    
    if (os_pthread_create("gun", &gun_thread, gun_task, NULL,  OS_TASK_PRIO_NOMAL, OS_STACK_SIZE_MEDIUM) != 0){
        ret = -1;
    }  
    return ret;
}

